#include "main.h"

#define FALSE  -1  
#define TRUE    0

#ifdef debugprintf
	#define debugpri(mesg, args...) fprintf(stderr, "[HI Serial print:%s:%d:]  "mesg"   \n", __FILE__, __LINE__, ##args) 
#else
	#define debugpri(mesg, args...)
#endif

typedef char u8;
typedef int u32;
struct DateTime{
	int year;
	int month;
	int date;
	int hour;
	int min;
	int sec;
};

typedef struct {
	char nshemi;//纬度方向
	float latitude;//纬度
	char ewhemi;//经度方向
	float longitude;//经度
	struct DateTime utc;	
}GPS_Msg;

/*
函数功能：从buf里面得到第cnt个逗号所在的位置
返 回 值：0~254,代表逗号所在位置的偏移.
255,代表不存在第cnt个逗号
*/
u8 GPS_GetCommaOffset(u8 *buf,u8 cnt)
{
	u8 *p=buf;
	while(cnt)
	{
		if(*buf=='*'||*buf<' '||*buf>'z')return 255;//遇到'*'或者非法字符,则不存在第cx个逗号
		if(*buf==',')cnt--;
		buf++;
	}
	return buf-p; //计算偏移量
}

/*
函数功能：m^n函数
返 回 值：m^n次方.
*/
u32 GPS_GetPow(u8 m,u8 n)
{
	u32 tmp=1;
	while(n--)tmp*=m;
	return tmp;
}

/*
函数功能：str转换为数字,以','或者'*'结束
函数参数：buf:数字存储区
		 dx:小数点位数,返回给调用函数
返 回 值：转换后的整数数值
*/
int GPS_StrtoNum(u8 *buf,u8*dx)
{
	u8 *p=buf;
	u32 ires=0,fres=0;
	u8 ilen=0,flen=0;
	u8 mask=0;
	int i;
	int res;
	while(1) //得到整数和小数的长度
	{
		if(*p=='-'){ mask|=0X02; p++; }//是负数
		if(*p==','||(*p=='*'))break;//遇到结束了
		if(*p=='.'){ mask|=0X01; p++; }//遇到小数点了
		else if(*p>'9'||(*p<'0'))	//有非法字符
		{
			ilen=0;
			flen=0;
			break;
		}
		if(mask&0X01)flen++;
		else ilen++;
		p++;
	}
	
	if(mask&0X02)buf++;	//去掉负号
	for(i=0; i<ilen; i++)	//得到整数部分数据
	{
		ires+=GPS_GetPow(10,ilen-1-i)*(buf[i]-'0');
	}
	if(flen>5)flen=5;	//最多取5位小数
	*dx=flen;	 		//小数点位数
	for(i=0; i<flen; i++)	//得到小数部分数据
	{
		fres+=GPS_GetPow(10,flen-1-i)*(buf[ilen+1+i]-'0');
	}
	res=ires*GPS_GetPow(10,flen)+fres;
	if(mask&0X02)res=-res;
	return res;
}


/*
函数功能：分析GNRMC信息
函数参数：GPS_DecodingInfo:nmea信息结构体
		 buf:接收到的GPS数据缓冲区首地址
*/
int GPS_GNRMC_InfoGet(GPS_Msg *GPS_DecodingInfo,u8 *buf)
{
	u8 *p1,dx;
	u8 posx;
	u32 temp;
	int lat,lon;
	float rs;
	char status;
	p1=(u8*)strstr((const char *)buf,"$GNRMC");
	if(!p1)return -1; //没有查找成功
	posx=GPS_GetCommaOffset(p1,2);								//南纬还是北纬 
	if(posx!=0XFF) status=*(p1+posx);
	if(status == 'V')
		return 0;
	posx=GPS_GetCommaOffset(p1,1);								//得到UTC时间
	if(posx!=0XFF)
	{
		temp=GPS_StrtoNum(p1+posx,&dx)/GPS_GetPow(10,dx);	 	//得到UTC时间,去掉ms
		GPS_DecodingInfo->utc.hour=temp/10000;
		GPS_DecodingInfo->utc.min=(temp/100)%100;
		GPS_DecodingInfo->utc.sec=temp%100;
	}
	posx=GPS_GetCommaOffset(p1,3);								//得到纬度  3447.51861
	if(posx!=0XFF)
	{
		temp=GPS_StrtoNum(p1+posx,&dx);   //344751861
		lat=temp/GPS_GetPow(10,dx+2);	//得到°      34
		rs=((float)(temp%GPS_GetPow(10,dx+2)))/GPS_GetPow(10,dx)/60;				//得到'	  	 
		GPS_DecodingInfo->latitude=lat+rs;//转换为° 
	}
	posx=GPS_GetCommaOffset(p1,4);								//南纬还是北纬 
	if(posx!=0XFF)GPS_DecodingInfo->nshemi=*(p1+posx);
	posx=GPS_GetCommaOffset(p1,5);								//得到经度
	if(posx!=0XFF)
	{
		temp=GPS_StrtoNum(p1+posx,&dx);
		lon=temp/GPS_GetPow(10,dx+2);	//得到°
		rs=((float)(temp%GPS_GetPow(10,dx+2)))/GPS_GetPow(10,dx)/60;				//得到'		 
		GPS_DecodingInfo->longitude=lon+rs;//转换为° 
	}
	posx=GPS_GetCommaOffset(p1,6);								//东经还是西经
	if(posx!=0XFF)GPS_DecodingInfo->ewhemi=*(p1+posx);
	posx=GPS_GetCommaOffset(p1,9);								//得到UTC日期
	if(posx!=0XFF)
	{
		temp=GPS_StrtoNum(p1+posx,&dx);		 				//得到UTC日期
		GPS_DecodingInfo->utc.date=temp/10000;
		GPS_DecodingInfo->utc.month=(temp/100)%100;
		GPS_DecodingInfo->utc.year=2000+temp%100;
	}
	return 1;
}

int gps_server_connect(void)
{
    int ret = 0;
    //1.创建通信套接子
    int clifd = socket(AF_INET,SOCK_STREAM,0);
    if(clifd <= 0)
    {
        printf("客户端套接子创建失败\n");
        return -1;
    }

    //2.创建连接核心结构体
    struct sockaddr_in serinfo;
    serinfo.sin_addr.s_addr = inet_addr(SERVER_IP);   //IP
    serinfo.sin_family = AF_INET;                   //IP类型
    serinfo.sin_port = htons(GPS_SERVER_PORT);        //端口号
    
    int opt = 1;
    setsockopt(clifd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
    setsockopt(clifd,SOL_SOCKET,SO_REUSEPORT,&opt,sizeof(opt));
    //3.连接服务器
    ret = connect(clifd,(struct sockaddr *)&serinfo,sizeof(serinfo));
    if(ret < 0)
    {
        printf("服务器连接失败\n");
        return -1;
    }
    printf("GPS服务器连接成功\n");
	
    return clifd;
}

void *pthread_gps_read(void * arg)
{
   	char SerialDev[32]="/dev/ttyS3";
	arg = arg;
	int fd;
		
	//O_NOCTTY：告诉Unix这个程序不想成为“控制终端”控制的程序，
	//			不说明这个标志的话，任何输入都会影响你的程序。
	//O_NDELAY：告诉Unix这个程序不关心DCD信号线状态，
	//			即其他端口是否运行，不说明这个标志的话，该程序就会在DCD信号线为低电平时停止。
	fd = open(SerialDev, O_RDWR|O_NOCTTY|O_NDELAY);  
	if (FALSE == fd)  
	{  
		perror("HiSerial Can't Open Serial HiSerDevice");  
		return NULL;  
	}  

	//恢复串口为阻塞状态                                 
	if(fcntl(fd, F_SETFL, 0) < 0)  
	{  
		debugpri("fcntl failed!\n");  
		return NULL;  
	}       
	else  
	{  
		debugpri("fcntl=%d\n",fcntl(fd, F_SETFL,0));  
	}  

	//测试是否为终端设备      
	if(0 == isatty(STDIN_FILENO))  
	{  
		debugpri("standard input is not a terminal device\n");  
		return NULL;  
	}  
	else  
	{  
		debugpri("isatty success!\n");  
	}                

	printf("fd->open=%d\n",fd);
	
	struct termios options;
	if(tcgetattr(fd,&options)  !=  0) //获取终端相关参数 
	{  
		perror("SetupSerial 1");      
		return NULL;   
	}

	//set buater rate
	cfsetispeed(&options, 9600);   //配置串口的输入波特率
	cfsetospeed(&options, 9600);   //配置串口的输出波特率
	
	//set flow control
	options.c_cflag &= ~CRTSCTS;  //不使用硬件流控制
	
	//select data bit   
    options.c_cflag &= ~CSIZE;  //清除串口的数据位
	options.c_cflag |= CS8;  	//配置串口的数据位为8位
	
	//select parity bit
	options.c_cflag &= ~PARENB;  //配置串口的校验位：无校验 
    options.c_iflag &= ~INPCK;  //不进行输入奇偶校验
	
	// set stopbit
	options.c_cflag &= ~CSTOPB; //配置的停止位：1位
	
	//set raw data output 
	//使用原始输出，就是禁用输出处理，使数据能不经过处理、过滤地完整地输出到串口接口。当OPOST被禁止，c_oflag其它选项也被忽略
	options.c_oflag &= ~OPOST;  

	//选择原始模式，在原始模式下，串口输入数据是不经过处理的，在串口接口接收的数据被完整保留
	options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);

	//set wait time
	options.c_cc[VTIME] = 100;    
    options.c_cc[VMIN] = 10;
	
	tcflush(fd,TCIFLUSH);  //清除终端未完成的输入/输出请求及数据、刷新缓存

    //set the attribute to HiSerial device 
    if (tcsetattr(fd,TCSANOW,&options) != 0)    //配置串口属性
	{  
		perror("com set error!\n");    
		return NULL;   
	} 
	
	int fs_sel;
	fd_set fs_read;
	FD_ZERO(&fs_read);  
    FD_SET(fd,&fs_read);
	
	struct timeval time;
	time.tv_sec = 30;
	time.tv_usec = 0;
     
	
	char rcv_buf[1024]={0};
	int len;
	GPS_Msg msg;
	int msg_ret;

	char snd_buf[256] = {0};

	while(1)
	{
		fs_sel = select(fd+1,&fs_read,NULL,NULL,&time);  
		if(fs_sel)  
		{  
			len = read(fd,rcv_buf,sizeof(rcv_buf));
			if(len > 0){
				//printf("Hiserial receive data: %s\n",rcv_buf);
				msg_ret = GPS_GNRMC_InfoGet(&msg,rcv_buf);
				if(msg_ret == 0){
					printf("GPS no signal!!!\n");				
				}
				else if(msg_ret == 1){
					if(gps_skd == 0)
					{
						gps_skd = gps_server_connect();
						if(gps_skd <= 0)
							printf("GPS服务器连接失败\r\n");
						else
							printf("GPS服务器连接成功\r\n");
					}
					else
					{
						sprintf(snd_buf,"{\
\"deviceId\":%d,\
\"longitude\":%.4f,\
\"latitude\":%.4f,\
\"gas\":%d,\
\"HatState\":%d,\
\"attitude\":%d,\
\"electricity\":%d,\
\"HighWarning\":%d,}"\
,h_info.device_id,msg.longitude,msg.latitude,-1,-1,!blance_flag,-1,-1);
						//sprintf(snd_buf,"%.4f,%.4f",msg.longitude,msg.latitude);
						write(gps_skd,snd_buf,sizeof(snd_buf));
						printf("GPS消息发送成功\r\n");
					}
					//printf("~~~datatime:%d-%d-%d %d:%d:%d jing:%.4f wei:%.4f\n",msg.utc.year,msg.utc.month,msg.utc.date,msg.utc.hour,msg.utc.min,msg.utc.sec,msg.longitude,msg.latitude);  
				}
				memset(rcv_buf,0,sizeof(rcv_buf));
				memset(&msg,0,sizeof(msg));
			}
			FD_SET(fd,&fs_read);  
		}  
		else  
		{  
			debugpri("Hiserial haven't data receive!");  
			return NULL;  
		}
		
		sleep(1);
	}
	
	return 0;

}

